package models;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import play.data.validation.*;
import play.db.jpa.Model;
import java.util.*;

import javax.persistence.*;

import org.apache.commons.mail.EmailException;

import controllers.Entities;

import models.*;
import play.db.jpa.*;

@Entity
public class Organization extends Model {

	@Required
	private String name;
	@Required
	private String description;
	@Required
	private String privacy;
	@Required
	@ManyToMany
	private List<User> members;
	// Todo U have to add this in the ERD or Remove it form the class Digram
	@Required
	private LinkedList<Relationship> relations;
	@OneToMany (mappedBy = "relatedOrganization", cascade=CascadeType.ALL)
	public List<Role> roles;
	// Relationship #1 Created_By between Organization And User
	// The Creator id form User of the Organization_IK
	// ToDO To be changed in the class digram Both type and the name_IK
	
	// Relationship #2 Belongs_to between Organization And Entity
	// ToDo Here Cascade the Relation
	@Required
	@OneToMany (mappedBy = "organization", cascade=CascadeType.ALL)
	private List<Eentity> entities;
	// Relationship #3 Belongs_to between Organization And Tag
	// All related Tags in this Organization_IK
	@Required
	@ManyToMany (mappedBy = "taggedOrganizations", cascade=CascadeType.ALL) 
	private List<Tag> tags;
	// Relationship #4 Belongs_To between Organization And Topic
	@Required
	@OneToMany (mappedBy = "organization", cascade=CascadeType.ALL)
	private List<Topic> topics;
	// Relationship #5 Belongs_To between Organization And Idea
	@Required
	@OneToMany (mappedBy = "organization",  cascade=CascadeType.ALL)
	private List<Idea> ideas;
	// Relationship #6 Info_About between Organization And Invitation
	@Required
	@OneToMany (mappedBy = "assignedOrganization",  cascade=CascadeType.ALL)
	private List<Invitation> invitation;
	// Relationship #7 Join between Organization And Type
	// TODO Chenikg the Erd
	// @Required
	// @ManyToMany
	@Required
	private LinkedList<Request> requests;
	@Required
	private boolean canBeJoined;
	@Required
	private boolean canRequestToJoin;
	@Required
	private boolean canInviteToJoin;
	@Required 
	private boolean canBefollowed;
	@Required 
	@OneToMany(mappedBy = "organization", cascade = CascadeType.ALL)
	private List<Plan> plans;

	/**
	 * Contstructor
	 * 
	 * @author Ibrahim Kessba
	 * @version 2011.0424
	 * @category C2 Modles
	 * @return None Number of Relations: 10
	 */

	public Organization() {
		
		entities = new LinkedList<Eentity>();
		tags = new LinkedList<Tag>();
		topics = new LinkedList<Topic>();
		ideas = new LinkedList <Idea>();
		invitation = new LinkedList <Invitation>();
		setRequests(new LinkedList <Request>());
		relations = new LinkedList<Relationship>();
		members= new LinkedList<User>();
		plans = new LinkedList <Plan>();
		

	}
	
	public Organization(String name, User lead, String description,
		String privacy) {
		
		this.name = name;
		members = new LinkedList<User>();
		new Role(this, "Lead");
		new Role(this, "Member");
		members.add(lead);
		this.description = description;
		this.setPrivacyToOrganization(privacy);
		
		entities = new LinkedList<Eentity>();
		tags = new LinkedList<Tag>();
		topics = new LinkedList<Topic>();
		ideas = new LinkedList <Idea>();
		invitation = new LinkedList <Invitation>();
		setRequests(new LinkedList <Request>());
		relations = new LinkedList<Relationship>();
		plans = new LinkedList <Plan>();

	}

	/**
	 * get all entities related to a certain entity
	 * 
	 * @author Mohamed Shalaby
	 * @version 2011.04.24
	 * @category C2 D4
	 * @param the
	 *            entity
	 * @return List: of entities that related to a certain entity
	 */

	public LinkedList<Eentity> getRelatedEntities(Eentity e) {

		return e.getRelatedEntities();
	}
	
	
	
	/**
	 * get all Tags related to a certain Organiztion
	 * 
	 * @author Ibrahim kessba
	 * @version 2011.04.02
	 * @category C2S13
	 * @param th Tag
	 * @return List: of tags that related to a certain Organization
	 */

	/*public LinkedList<Tag> getRelatedTags(Tag t) {
			
		return getAllTags();
		
	}*/
	

	/**
	 * get all users in Organization
	 * 
	 * @author Mohamed Shalaby
	 * @version 2011.04.24
	 * @category C2 D7
	 * @param no
	 *            parameters
	 * @return List : of all users in the Organization
	 */

	public LinkedList<User> getAllUsersInOrganization() {
		return this.getMembers();
	}

	/**
	 * Gets all entities in the organization
	 * 
	 * @author Ebtehal Abed
	 * @version 2011.0425
	 * @category C2D5
	 * @params no parameters
	 * @return List: a list of all entities in an organization
	 */
	
	public List<Eentity> getAllEntities() {

		return entities;
	}

	/**
	 * Gets all tags in the organization
	 * 
	 * @author Ebtehal Abed
	 * @version 2011.0425
	 * @category C2D5
	 * @params no parameters
	 * @return List: a list of all tags in an organization
	 */

	public LinkedList<Tag> getAllTags() {

		return (LinkedList<Tag>) this.tags;
	}

	/**
	 * Gets all topics in the organization
	 * 
	 * @author Ebtehal Abed
	 * @version 2011.0425
	 * @category C2D5
	 * @params no parameters
	 * @return List: a list of all topics in an organization
	 */

	public List<Topic> getAllTopics() {

		//List alltopics=   Topic.findAll();
		return this.topics;
		
	}

	/**
	 * 3 methods that gets all organizers, followers, members and lead
	 * 
	 * @author Ebtehal Abed
	 * @version 2011.04.29
	 * @return: List of organizers, List of followers, List of members
	 			User Lead
	 */
	
	public LinkedList<User> getOrganizers() {
		LinkedList<User> organisers  = new LinkedList<User>();
		
		
		for (int i=0;i<members.size();i++)
		{
			for(int j = 0; j< members.get(i).getRoleList().size(); j++) {
				if (members.get(i).getRoleList().get(j).getTitle().equalsIgnoreCase("Organizer") &&
						(members.get(i).getRoleList().get(j).getRelatedOrganization() != null)
					&&	members.get(i).getRoleList().get(j).getRelatedOrganization().equals(this)) {
					organisers.add(members.get(i));
				}
			}
			
		}
		return organisers;
	}
	
	public LinkedList<User> getFollowers() {
		LinkedList<User> followers  = new LinkedList<User>();
		
		
		for (int i=0;i<members.size();i++)
		{
			for(int j = 0; j< members.get(i).getRoleList().size(); j++) {
				if (members.get(i).getRoleList().get(j).getTitle().equalsIgnoreCase("Follower") &&
						(members.get(i).getRoleList().get(j).getRelatedOrganization() != null)
					&&	members.get(i).getRoleList().get(j).getRelatedOrganization().equals(this)) {
					followers.add(members.get(i));
				}
			}
			
		}
		return followers;
	}
	
	public LinkedList<User> getMembers() {
		LinkedList<User> members  = new LinkedList<User>();
		
		
		for (int i=0;i<members.size();i++)
		{
			for(int j = 0; j< members.get(i).getRoleList().size(); j++) {
				if (members.get(i).getRoleList().get(j).getTitle().equalsIgnoreCase("Member") &&
						(members.get(i).getRoleList().get(j).getRelatedOrganization() != null)
					&&	members.get(i).getRoleList().get(j).getRelatedOrganization().equals(this)) {
					members.add(members.get(i));
				}
			}
			
		}
		return members;
	}
	
	/**
	 * check if member of Organization
	 * 
	 * @author Amr Draz
	 * @version 2011.05.02
	 * @category C4S14
	 * @param user: user to be checked
	 * @return boolean :is a member
	 */

	public boolean isMember(User user) {
		
		LinkedList<User> m = getMembers();
		for (int i = 0; i < m.size(); i++) {
			if(m.get(i).id == user.id)
				return true;
		}
		return false;
	}
	
	/**
	 * check if Organization is private
	 * 
	 * @author Amr Draz
	 * @version 2011.05.02
	 * @category C4S14
	 * @param none
	 * @return boolean :is private
	 */

	public boolean isPrivate() {		
		return this.getPrivacy().equalsIgnoreCase("Privat");
	}
	
	public User getLead() {
		for (int i=0;i<members.size();i++)
		{
			for(int j = 0; j< members.get(i).getRoleList().size(); j++) {
				if (members.get(i).getRoleList().get(j).getTitle().equalsIgnoreCase("Lead") &&
						(members.get(i).getRoleList().get(j).getRelatedOrganization() != null)
					&&	members.get(i).getRoleList().get(j).getRelatedOrganization().equals(this)) {
				return members.get(i);
				}
			}
			
		}
		return null;
	}
	
	/**
	 * This methods enable Organizers and Organization leads to participate as idea's developers by adding to them a role Member
	 * @author Nirmeen Sallam
	 * @version 2011.0503
	 * @category C1S9
	 * @params none	
	 * 
	 * @return void
	 */
	
	public void setAllMembers (){
		List<User> members = getMembers() ;
		for (int i = 0 ; i < members.size();i++){
			if (!members.get(i).getRoleList().equals("Member")){

				members.get(i).getRoleList().add(new Role(this, "Member"));
				
			}
		}
	}
	
	
	

	/**
	 * 3 methods that sets organizers, followers, members and lead
	 * 
	 * @author Ebtehal Abed
	 * @version 2011.04.29
	 * @return: void
	 */
	
	public void setLead (User lead){
		new Role(this,"Lead");
		members.add(lead);
	}
	
	public void setOrganizers(User organizer) {
		new Role(this,"Organizer");
		members.add(organizer);
	}
	
	public void setFollowers(User follower) {
		new Role(this,"Follower");
		members.add(follower);
	}
	
	public void setMembers (User member){
		new Role(this, "Member");
		members.add(member);
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String discription) {
		this.description = discription;
	}

	public List<Relationship> getRelations() {
		return (List<Relationship>) relations;
	}

	public void setRelations(LinkedList<Relationship> relations) {
		this.relations = relations;
	}

	public void setEntites(LinkedList<Eentity> entities) {
		this.entities = entities;
	}

	public LinkedList<Tag> getTags() {
		return (LinkedList<Tag>) tags;
	}

	public void setTags(LinkedList<Tag> tags) {
		this.tags = tags;
	}

	public List<Topic> getTopics() {
		return  topics;
	}

	public void setTopics(LinkedList<Topic> topics) {
		this.topics = topics;
	}

	public List<Idea> getIdeas() {
		return  ideas;
	}

	public void setIdeas(LinkedList<Idea> ideas) {
		this.ideas = ideas;
	}

	public LinkedList<Invitation> getInvitation() {
		return (LinkedList<Invitation>) invitation;
	}

	public void setInvitation(LinkedList<Invitation> invitation) {
		this.invitation = invitation;
	}

	public void setMembers(LinkedList<User> members) {
		this.members = members;
	}
	public void setRequests(LinkedList<Request> requests) {
		this.requests =  requests;
	}
	public List<Request> getRequests() {
		return requests;
	}
	
	/**
	 * a method that sets the privacy of a certain organization as public,
	 * private or secret, also it sets the actions that the user will be allowed to do in each case
	 * 
	 * 
	 * @author Ebtehal Abed
	 * @version 2011.05.01
	 * @category C2S3
	 * @param String privacy (public, private, secret)
	 * @return void (nothing it just sets the verbs could be done on the organization)
	 */
	
	public void setPrivacyToOrganization(String privacy) {
		this.privacy = privacy;
		
		if (privacy == "Public") {
			this.setCanBeJoined(true);
			this.setCanRequestToJoin(true);
			this.setCanInviteToJoin(true);
			this.setCanBefollowed(true);
		} else {
			if (privacy == "Private") {
				this.setCanBeJoined(false);
				this.setCanRequestToJoin(true);
				this.setCanInviteToJoin(true);
				this.setCanBefollowed(false);
			} else {
				if (privacy == "Secret") {
					this.setCanBeJoined(false);
					this.setCanRequestToJoin(false);
					this.setCanInviteToJoin(true);
					this.setCanBefollowed(false);
				}
			}
		}
	}
	public String getPrivacy() {
		return privacy;
	}
	public void setCanBeJoined(boolean canBeJoined) {
		this.canBeJoined = canBeJoined;
	}
	public boolean isCanBeJoined() {
		return canBeJoined;
	}
	public void setCanRequestToJoin(boolean canRequestToJoin) {
		this.canRequestToJoin = canRequestToJoin;
	}
	public boolean isCanRequestToJoin() {
		return canRequestToJoin;
	}
	public void setCanInviteToJoin(boolean canInviteToJoin) {
		this.canInviteToJoin = canInviteToJoin;
	}
	public boolean isCanInviteToJoin() {
		return canInviteToJoin;
	}
	public void setCanBefollowed(boolean canBefollowed) {
		this.canBefollowed = canBefollowed;
	}
	public boolean isCanBefollowed() {
		return canBefollowed;
	}

	public void setPlans(List<Plan> plans) {
		this.plans = plans;
	}

	public List<Plan> getPlans() {
		return plans;
	}

	public void addMember(User user) {
		if(!has(user)) {
			System.out.println("adding " + user.getUsername());
			user.save();
			members.add(user);
			user.save();
			this.save();
			user.addOrganization(this);
		}
	}

	public void addIdea(Idea idea) {
		if(!has(idea)) {
			ideas.add(idea);
			idea.setOrganization(this);
		}
	}

	public void addEntity(Eentity entity) {
		if (!has(entity)) {
			entities.add(entity);
			entity.setOrganization(this);
		}
	}

	private boolean has(Eentity entity) {
		for(int i = 0;i < entities.size();i++)
			if(entities.get(i).id == entity.id)
				return true;
		return false;
	}

	public void addTopic(Topic topic) {
		if(!has(topic)) {
			topics.add(topic);
			topic.setOrganization(this);
		}
	}

	public void addTag(Tag tag) {
		if(!has(tag)) {
			tags.add(tag);
			tag.addOrganization(this);
		}
	}
	
	/**
	 * check if topic is in Organization
	 * 
	 * @author Amr Draz
	 * @version 2011.05.06
	 * @category C4S14
	 * @param topic: topic to be checked
	 * @return boolean :topic is already in organization
	 */
	
	private boolean has(Topic topic) {
		for(int i = 0;i < topics.size();i++)
			if(topics.get(i).id == topic.id)
				return true;
		return false;
	}
	
	/**
	 * check if tag is is in Organization
	 * 
	 * @author Amr Draz
	 * @version 2011.05.06
	 * @category C4S14
	 * @param tag: tag to be checked
	 * @return boolean :tag is already in organization's tags list
	 */
	
	private boolean has(Tag tag) {
		for(int i = 0;i < tags.size();i++)
			if(tags.get(i).id == tag.id)
				return true;
		return false;
	}
	
	/**
	 * check if idea is in Organization
	 * 
	 * @author Amr Draz
	 * @version 2011.05.06
	 * @category C4S14
	 * @param idea: idea to be checked
	 * @return boolean :idea is already in organization's ideas list
	 */
	
	private boolean has(Idea idea) {
		for(int i = 0;i < ideas.size();i++)
			if(ideas.get(i).id == idea.id)
				return true;
		return false;
	}
	
	/**
	 * check if member is in Organization
	 * 
	 * @author Amr Draz
	 * @version 2011.05.06
	 * @category C4S14
	 * @param user: member to be checked
	 * @return boolean :user is already in organization's member list
	 */
	
	private boolean has(User user) {
		System.out.println("checking if "+user.getUsername()+" is in org");
		for(int i = 0;i < members.size();i++) {
			if(members.get(i).id == user.id) {
				System.out.println(user.getUsername() +" is already in org");
				return true;
			}
		}
		System.out.println(user.getUsername() + "is not a member");
		return false;
	}

	public List<Eentity> getEntites() {
		return entities;
	}

}